﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Drawing;
//MicroDesigner
using Jc.MicroDesigner.Objects;
using Jc.MicroDesigner.Management;

namespace Jc.MicroDesigner.Drawings.Tools
{
    /// <summary>
    /// Инструмент - провод
    /// </summary>
    public sealed class WireTool : Drawings.DrawingTool
    {
        Wire _CreatingWire = null;
        Panel _Panel = null;
        Point _MousePoint;
        Point _Middle;

        bool _LastActive = false;

        /// <summary>
        /// Минимальный интервал между средней точкой моделирования и конечной точкой
        /// </summary>
        const int MinDeltaMiddle = 20;

        private void AddPointToWire(DrawPanel panel, Point e)
        {
            if (_CreatingWire != null)
            {
                BaseElement el = panel.SearchElementByXY(e.X, e.Y);
                //если создаётся провод и он ещё не был добавлен в элементы для отрисовки
                if ((panel.ObjManager != null) && (!panel.ObjManager.Elements.Contains(_CreatingWire)))
                {
                    _CreatingWire.Name = panel.ObjManager.NewElementName(typeof(Wire));
                    panel.ObjManager.Elements.Add(_CreatingWire);
                }
                if (el != null)
                {
                    //Добавить к элементу соединение
                    if (el is CircuitElement)
                    {
                        PIN pin = el.DrawOptions.GetBindingPIN(e.X, e.Y);
                        if (pin != null)
                        {
                            Point p = (pin.DrawOptions as PIN.PINImage).BindingPoint;
                            #region Last

                            p.X += el.DrawOptions.Left;
                            p.Y += el.DrawOptions.Top;

                            Wire.WireImage img = _CreatingWire.DrawOptions as Wire.WireImage;
                            Point last = img.Points[img.Points.Count - 1];
                            int x_diff = Math.Abs(last.X + img.Left - p.X);
                            int y_diff = Math.Abs(last.Y + img.Top - p.Y);

                            _CreatingWire.DrawOptions.RefreshTemplate();

                            bool added = false;
                            int delta = 4;
                            // Провода строятся только под прямым углом и если попадут в нужную область
                            if (x_diff >= y_diff)
                            {
                                if (Math.Abs(last.Y + img.Top - p.Y) < delta)
                                {
                                    added = true;
                                    img.AddPoint(p.X, last.Y + img.Top);
                                }
                            }
                            else
                            {
                                if (Math.Abs(last.X + img.Left - p.X) < delta)
                                {
                                    added = true;
                                    img.AddPoint(last.X + img.Left, p.Y);
                                }
                            }

                            #endregion

                            panel.Invalidate();

                            if ((pin != null) && (pin.FriendPIN == null) && added)
                            {
                                PIN.Unite(_CreatingWire.PINs[1], pin);
                                if (_CreatingWire != null)
                                    CreateEndPoint();
                                //на этом создание провода прекращается
                                _CreatingWire = null;
                            }
                        }
                    }
                }
                else
                {
                    Wire.WireImage img = _CreatingWire.DrawOptions as Wire.WireImage;
                    Point last = img.Points[img.Points.Count - 1];
                    int x_diff = Math.Abs(last.X + img.Left - e.X);
                    int y_diff = Math.Abs(last.Y + img.Top - e.Y);

                    _CreatingWire.DrawOptions.RefreshTemplate();

                    // Провода строятся только под прямым углом
                    if (x_diff >= y_diff)
                        img.AddPoint(e.X, last.Y + img.Top);
                    else
                        img.AddPoint(last.X + img.Left, e.Y);
                }
                panel.Invalidate();
            }
        }

        /// <summary>
        /// Создаёт пустой провод с точкой приложения (X,Y)
        /// </summary>
        /// <param name="x">X</param>
        /// <param name="y">Y</param>
        private void CreateBasePoint(Point p)
        {
            _CreatingWire = new Wire("NewWire");
            Wire.WireImage img = _CreatingWire.DrawOptions as Wire.WireImage;
            img.Left = p.X - 2;
            img.Top = p.Y - 2;
            //в коллекцию добавляются точки с относительными координатами
            img.AddPoint(p.X, p.Y);
            _CreatingWire.DrawOptions.RefreshTemplate();
        }

        /// <summary>
        /// Закончить построение провода
        /// </summary>
        /// <param name="x">X</param>
        /// <param name="y">Y</param>
        private void CreateEndPoint()
        {
            Wire.WireImage img = _CreatingWire.DrawOptions as Wire.WireImage;
            Point end = img.Points[img.Points.Count - 1];
            (_CreatingWire.PINs[1].DrawOptions as PIN.PINImage).BindingPoint = new Point(end.X, end.Y);
            _CreatingWire.DrawOptions.RefreshTemplate();
        }

        /// <summary>
        /// Создать инструмент-провод
        /// </summary>
        public WireTool()
        {
            this._Name = "Провод";
            this._ToolCursor = Cursors.Cross;
        }

        /// <summary>
        /// Нажатие клавиш мыши
        /// </summary>
        /// <param name="panel"></param>
        /// <param name="e"></param>
        public override void OnMouseDown(DrawPanel panel, MouseEventArgs e)
        {
            _Panel = panel;
            switch (e.Button)
            {
                //задаём точку начало провода
                case MouseButtons.Left:
                    if (_CreatingWire != null)
                    {
                        //если точек более одной, то закончить построение
                        Wire.WireImage img = _CreatingWire.DrawOptions as Wire.WireImage;
                        if (img.Points.Count > 1)
                        {
                            CreateEndPoint();
                            panel.Invalidate();
                        }
                        else
                            _CreatingWire.DisconnectAll();
                    }

                    _CreatingWire = null;
                    BaseElement el = panel.SearchElementByXY(e.X, e.Y);
                    if (el != null)
                    {
                        if (el is CircuitElement)
                        {
                            //Добавить к элементу соединение
                            PIN pin = el.DrawOptions.GetBindingPIN(e.X, e.Y);
                            if ((pin != null) && (pin.FriendPIN == null))
                            {
                                Point p = (pin.DrawOptions as PIN.PINImage).BindingPoint;
                                p.X += el.DrawOptions.Left;
                                p.Y += el.DrawOptions.Top;
                                CreateBasePoint(p);
                                PIN.Unite(_CreatingWire.PINs[0], pin);
                            }
                            else
                                if (el is Wire)
                                {
                                    //Порвать провод, добавить узел
                                    Wire elwire = el as Wire;
                                    Wire.WireImage elimage = el.DrawOptions as Wire.WireImage;
                                    //две половинки провода
                                    Wire left = new Wire("Wire");
                                    Wire right = new Wire("Wire");
                                    int barier = elimage.NearestWirePoint(e.X, e.Y);

                                    #region Cоздаём первую половинку провода

                                    left.PINs[0] = elwire.PINs[0];
                                    elwire.PINs[0].DropEvents();
                                    left.UpdateEvents();
                                    left.PINs[0].Parent = left;
                                    Wire.WireImage leftimage = left.DrawOptions as Wire.WireImage;
                                    leftimage.Width = elimage.Width;
                                    leftimage.Height = elimage.Height;
                                    leftimage.Left = elimage.Left;
                                    leftimage.Top = elimage.Top;

                                    for (int i = 0; i <= barier; i++)
                                        leftimage.Points.Add(new Point(elimage.Points[i].X, elimage.Points[i].Y));

                                    Point last = elimage.Points[barier];
                                    int x_diff = Math.Abs(last.X + leftimage.Left - e.X);
                                    int y_diff = Math.Abs(last.Y + leftimage.Top - e.Y);

                                    // Провода строятся только под прямым углом
                                    if (x_diff >= y_diff)
                                        leftimage.AddPoint(e.X, last.Y + leftimage.Top);
                                    else
                                        leftimage.AddPoint(last.X + leftimage.Left, e.Y);

                                    Point end = leftimage.Points[leftimage.Points.Count - 1];
                                    left.PINs[1].DrawOptions.Left = end.X - 2;
                                    left.PINs[1].DrawOptions.Top = end.Y - 2;
                                    left.PINs[1].DrawOptions.Width = 5;
                                    left.PINs[1].DrawOptions.Height = 5;
                                    (left.PINs[1].DrawOptions as PIN.PINImage).BindingPoint = new Point(end.X, end.Y);

                                    #endregion

                                    #region Cоздаём вторую половинку провода

                                    right.PINs[1] = elwire.PINs[1];
                                    elwire.PINs[1].DropEvents();
                                    right.UpdateEvents();
                                    right.PINs[1].Parent = right;
                                    Wire.WireImage rightimage = right.DrawOptions as Wire.WireImage;
                                    rightimage.Width = elimage.Width;
                                    rightimage.Height = elimage.Height;
                                    rightimage.Left = elimage.Left;
                                    rightimage.Top = elimage.Top;

                                    // Провода строятся только под прямым углом
                                    if (x_diff >= y_diff)
                                        rightimage.AddPoint(e.X, last.Y + leftimage.Top);
                                    else
                                        rightimage.AddPoint(last.X + leftimage.Left, e.Y);

                                    for (int i = barier + 1; i < elimage.Points.Count; i++)
                                        rightimage.Points.Add(new Point(elimage.Points[i].X, elimage.Points[i].Y));

                                    Point begin = rightimage.Points[0];
                                    right.PINs[0].DrawOptions.Left = begin.X - 2;
                                    right.PINs[0].DrawOptions.Top = begin.Y - 2;
                                    right.PINs[0].DrawOptions.Width = 5;
                                    right.PINs[0].DrawOptions.Height = 5;
                                    (right.PINs[0].DrawOptions as PIN.PINImage).BindingPoint = new Point(begin.X, begin.Y);

                                    #endregion

                                    #region Добавляем узел

                                    Node node = new Node("Node");
                                    node.DrawOptions.Left = e.X - 2;
                                    node.DrawOptions.Top = e.Y - 2;
                                    node.AddWire(left.PINs[1]);
                                    node.AddWire(right.PINs[0]);

                                    pin = node.DrawOptions.GetBindingPIN(e.X, e.Y);
                                    Point p = (pin.DrawOptions as PIN.PINImage).BindingPoint;
                                    p.X += node.DrawOptions.Left;
                                    p.Y += node.DrawOptions.Top;
                                    CreateBasePoint(p);
                                    PIN.Unite(_CreatingWire.PINs[0], pin);

                                    #endregion

                                    #region Заменяем старый провод

                                    ObjectManager manager = panel.ObjManager;
                                    manager.Elements.Remove(el);
                                    node.Name = manager.NewElementName(node.GetType());
                                    manager.Elements.Add(node);

                                    left.Name = manager.NewElementName(left.GetType());
                                    manager.Elements.Add(left);

                                    right.Name = manager.NewElementName(right.GetType());
                                    manager.Elements.Add(right);

                                    #endregion

                                    panel.Invalidate();
                                }
                        }
                    }
                    else
                    {
                        //создать пустой провод
                        CreateBasePoint(e.Location);
                    }
                    break;

                case MouseButtons.Middle:
                    if (_CreatingWire != null)
                    {
                        //если точек более одной, то закончить построение
                        Wire.WireImage img = _CreatingWire.DrawOptions as Wire.WireImage;
                        if (img.Points.Count > 1)
                            CreateEndPoint();
                        else
                            _CreatingWire.DisconnectAll();
                        _CreatingWire = null;
                    }
                    panel.Invalidate();
                    break;
            }
        }

        /// <summary>
        /// Прекращение нажатия клавиш мыши
        /// </summary>
        /// <param name="panel"></param>
        /// <param name="e"></param>
        public override void OnMouseUp(DrawPanel panel, MouseEventArgs e)
        {
            switch (e.Button)
            {
                //создаём сегменты для провода
                case MouseButtons.Right:
                    AddPointToWire(panel, _Middle);
                    if (_LastActive)
                        AddPointToWire(panel, e.Location);
                    break;
            }
        }

        /// <summary>
        /// При перемещении мыши
        /// </summary>
        /// <param name="panel"></param>
        /// <param name="e"></param>
        public override void OnMouseMove(DrawPanel panel, MouseEventArgs e)
        {
            if (_CreatingWire != null)
            {
                _MousePoint = e.Location;
                panel.Invalidate();
            }
        }

        /// <summary>
        /// Отрисовка вспомогательных линий разметки провода
        /// </summary>
        /// <param name="panel"></param>
        /// <param name="g"></param>
        public override void OnPaintScheme(DrawPanel panel, Graphics g)
        {
            if (_CreatingWire != null)
            {

                Pen p = new Pen(Color.Indigo);
                p.DashStyle = System.Drawing.Drawing2D.DashStyle.Dash;
                p.Width = 1.5f;

                Wire.WireImage img = _CreatingWire.DrawOptions as Wire.WireImage;
                Point wire_point = img.Points[img.Points.Count - 1];
                Point last = new Point(wire_point.X + img.Left, wire_point.Y + img.Top);

                int diff_x = Math.Abs(_MousePoint.X - last.X);
                int diff_y = Math.Abs(_MousePoint.Y - last.Y);
                _LastActive = false;

                if (diff_x > diff_y)
                    _Middle = new Point(_MousePoint.X, last.Y);
                else
                    _Middle = new Point(last.X, _MousePoint.Y);

                g.DrawLine(p, last, _Middle);

                if (Math.Min(diff_x, diff_y) > MinDeltaMiddle)
                {
                    _LastActive = true;
                    g.DrawLine(p, _Middle, _MousePoint);
                }
            }
        }

        /// <summary>
        /// Деактивация инструмента
        /// </summary>
        public override void OnDeactivate()
        {
            if (_CreatingWire != null)
            {
                //если точек более одной, то закончить построение
                Wire.WireImage img = _CreatingWire.DrawOptions as Wire.WireImage;
                if (img.Points.Count > 1)
                {
                    CreateEndPoint();
                }
                else
                    _CreatingWire.DisconnectAll();
                _CreatingWire = null;
            }
            if (_Panel != null)
                _Panel.Invalidate();
        }
    }
}